package com.api.interceptor;

import com.api.MEnumError;
import com.api.MErrorEnum;
import com.api.anno.ApiAuth;
import com.api.anno.ApiUnCheck;
import com.base.Const;
import com.base.CoreConstants;
import com.base.api.ApiException;
import com.base.api.annotation.ApiMethod;
import com.base.cache.CacheSupport;
import com.base.cache.CacheUtil;
import com.base.pay.wx.util.MD5Util;
import com.base.util.DateUtil;
import com.base.util.RequestKit;
import com.base.util.StringUtil;
import com.base.web.ParameterRequestWrapper;
import com.item.ConstantsCode;
import com.item.dao.model.Admin;
import com.item.dao.model.MobileVerify;
import com.item.dao.model.User;
import com.item.service.AdminService;
import com.item.service.CodeService;
import com.item.service.MobileVerifyService;
import com.item.service.UserService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * 权限(Token)验证
 */
@Component
public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    private MobileVerifyService mobileVerifyService;
    @Autowired
    private UserService userService;
    @Autowired
    private AdminService adminService;
    @Autowired
    private CodeService codeService;

    private static final int NICK_LENGTH = 30;
    
    private static final String TOKEN = "token";
    private static final String USER_ID = "userid";
    private static final String DEVICE_ID = "deviceid";
    private static final String DEVICE_TYPE = "deviceType";

    private static final String TIMESTAMP = "_timestamp";
    private static final long API_TIME = 1000;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(handler instanceof HandlerMethod) {
        	ApiMethod annotation = ((HandlerMethod) handler).getMethodAnnotation(ApiMethod.class);
            if (annotation == null) return true;
            response.setHeader("Access-Control-Allow-Origin","*");
            //签名确认
            if (annotation.isAuth() && !checkTimeSign(request)){
                throw new ApiException(MErrorEnum.TIME_AUTH_FAIL_ERROR);
            }
            //需要签名认证的接口加上ApiAuth
            ApiAuth apiAuth = ((HandlerMethod) handler).getMethodAnnotation(ApiAuth.class);
            if (apiAuth != null){
                if (!authVerify(request)){
                    throw new ApiException(MErrorEnum.AUTH_FAIL_ERROR);
                }
            }

        	//从header中获取token
            String token = request.getHeader(TOKEN);
            //如果header中不存在token，则从参数中获取token
            if(StringUtils.isBlank(token)){
                token = request.getParameter(TOKEN);
            }
            
            String nickName = request.getParameter("nickName");
            if (StringUtils.isNotBlank(nickName)){
            	if (nickName.length() > NICK_LENGTH){
            		throw new ApiException(MEnumError.NICK_LEN_ERROR);
            	}
            }
            
            //分页
            if (annotation.isPage()){
            	String p = request.getParameter(Const.PAGE);
            	String l = request.getParameter(Const.LIMIT);
            	if (StringUtils.isBlank(p) || StringUtils.isBlank(l)){
            		throw new ApiException(MErrorEnum.PAGE_LIMIT_NONG);
            	}
            }
            
            MobileVerify mobileVerify = null;
            
            if (StringUtils.isNotBlank(token)){
            	mobileVerify = CacheSupport.get(CacheUtil.MOBILE_VERIFY_CACHE, token, MobileVerify.class);
            	if (mobileVerify == null){
            		mobileVerify = mobileVerifyService.queryByToken(token);
            		if (mobileVerify != null){
            			CacheSupport.put(CacheUtil.MOBILE_VERIFY_CACHE, token, mobileVerify);
            		}
            	}
            	
            }
            
            //登陆
            if(annotation.isLogin()){
                if(mobileVerify == null || (mobileVerify.getExpireTime() != null && mobileVerify.getExpireTime().getTime() < System.currentTimeMillis())){
            		throw new ApiException(MErrorEnum.LOGIN_FAIL_ERROR);
                }
                if (mobileVerify.getDeviceId().endsWith("@store")){
                    Admin store = adminService.selectByPrimaryKey(mobileVerify.getUserId());
                    if (store == null){
                        throw new ApiException(MErrorEnum.LOGIN_FAIL_ERROR);
                    }
                    if (store.getState() == 0){
                        throw new ApiException(-1,"账号被禁用");
                    }
                }else{
                    User user = userService.selectByPrimaryKey(mobileVerify.getUserId());
                    if (user == null){
                        throw new ApiException(MErrorEnum.LOGIN_FAIL_ERROR);
                    }
                    if (user.getState() == 0){
                        throw new ApiException(-1,"账号被禁用");
                    }
                    if(mobileVerify.getDeviceType() != 0){
                        ApiUnCheck unCheck = ((HandlerMethod) handler).getMethodAnnotation(ApiUnCheck.class);
                        if (user.getIsComplete() == 0 && unCheck == null){
                            //完善用户资料
                            throw new ApiException(MErrorEnum.COMPLETE_USER_INFO);
                        }
                    }
                }
            }
            
            if (mobileVerify != null){
        		if(mobileVerify.getExpireTime() != null){
            		Date date = new Date();
            		mobileVerify.setExpireTime(DateUtil.addMinute(date, 30));
            		mobileVerify.setUpdateTime(date);
            		mobileVerifyService.updateByPrimaryKey(mobileVerify);
            		CacheSupport.put(CacheUtil.MOBILE_VERIFY_CACHE, token, mobileVerify);
            	}
        		ParameterRequestWrapper wrapper =  (ParameterRequestWrapper) request;
            	wrapper.addParameter(USER_ID, mobileVerify.getUserId());
            	wrapper.addParameter(DEVICE_ID, mobileVerify.getDeviceId());
            	wrapper.addParameter(DEVICE_TYPE, mobileVerify.getDeviceType());
        	}
           
        }
        return true;
    }

    public boolean authVerify(HttpServletRequest request){
        Map<String,String> paramMap = RequestKit.getFromRequest(request);
        return authVerify(paramMap);
    }

    /**
     * 签名认证
     * @param paramMap
     * @return
     */
    public boolean authVerify(Map<String,String> paramMap){
        String key = codeService.getApiKey();
        if (StringUtil.isBlank(key)){
            return true;
        }
        String sign = paramMap.get(CoreConstants.SIGN);
        if (StringUtils.isBlank(sign)) return false;
        paramMap.put("key", key);
        String verifySign = createSign(new TreeMap<>(paramMap));
        return sign.equals(verifySign);
    }

    @SuppressWarnings("rawtypes")
    private String createSign(SortedMap<String, String> params){
        StringBuffer sb = new StringBuffer();
        Set es = params.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            String v = (String)entry.getValue();
            if(null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + params.get("key"));

        String sign = MD5Util.MD5Encode(sb.toString(), "utf-8").toUpperCase();

        return sign;
    }

    /**
     * 临时签名用
     * @param paramMap
     * @return
     */
    private String createTimeSign(Map<String,String> paramMap){
        return createSign(new TreeMap<>(paramMap));
    }

    private boolean checkTimeSign(HttpServletRequest request){
        Map<String,String> paramMap = RequestKit.getFromRequest(request);
        String sign = createTimeSign(paramMap);
        Long cacheTimeStamp = CacheSupport.get(ConstantsCode.API_SIGN_CACHE_NAME,sign,Long.class);
        if (cacheTimeStamp != null){
            long time = System.currentTimeMillis() - cacheTimeStamp;
            if (time < API_TIME){
                return false;
            }
        }
        CacheSupport.put(ConstantsCode.API_SIGN_CACHE_NAME,sign,System.currentTimeMillis());
        return true;
    }
}
